package com.adv.ems.utils;

import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.adv.ems.db.entity.CollectPointDO;
import com.adv.ems.db.entity.EnergyDataOriginalDO;
import com.adv.ems.model.dto.ExcelMeterInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class ScriptUtil {
    // 创建ScriptEngineManager
    private static final ScriptEngineManager manager = new ScriptEngineManager();
    // 获取一个ScriptEngine实例，这里使用JavaScript引擎
    private static final ScriptEngine engine = manager.getEngineByName("JavaScript");
    private static String pattern = "([\\{]{2}[0-9]*[\\}]{2})";
    private static String meterHolderPattern = "([\\{]{2}[^\\{ ^\\}]*[\\}]{2})";


    public static Set<Long> getVirtualPointRelatePointIds(String script) {
        Set<Long> pointIds = new HashSet<>();
        if (!StringUtils.hasLength(script)) {
            return pointIds;
        }
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(script);
        while (m.find()) {
            String group = m.group(1);
            pointIds.add(Long.valueOf(group.substring(2, group.length() - 2)));
        }
        return pointIds;
    }

    public static Double getProcessRealValue(EnergyDataOriginalDO data, CollectPointDO point) {
        if (data == null
                || point == null
                || data.getColData() == null
                || !StringUtils.hasLength(point.getComputeScript())) {
            return data == null ? Double.valueOf(0.0) : data.getColData();
        }
        Map<Long, Double> map = MapUtil.of(data.getCollectPointId(), data.getColData());
        return getVirtualValue(map, point);
    }

    public static Double getVirtualValue(Map<Long, Double> valueMap, CollectPointDO point) {
        if (point == null || point.getComputeScript() == null) {
            log.warn("虚拟点【{}】未配置计算脚本", point == null ? "null" : point.getId());
            return 0D;
        }
        String script = point.getComputeScript();
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(script);
        Map<String, Double> map = new HashMap<>();
        while (m.find()) {
            String group = m.group(1);
            Long pointId = Long.valueOf(group.substring(2, group.length() - 2));
            Double v = valueMap.get(pointId);
            if (v == null) {
                log.warn("虚拟点值【{}】计算失败，valueMap【{}】 parsePont【{}】", point.getId(), JSONUtil.toJsonStr(valueMap), pointId);
                v = 0D;
            }
            map.put(group, v);
        }
        for (Map.Entry<String, Double> entry : map.entrySet()) {
            String group = entry.getKey();
            Double value = entry.getValue();
            int loopTimes = 0;
            while (script.contains(group)) {
                if (loopTimes > 20) {
                    break;
                }
                script = script.replace(group, value.toString());
                loopTimes++;
            }
        }
        Object eval = null;
        try {
            eval = engine.eval(script);
            return Double.parseDouble(eval.toString());
        }catch (Exception e) {
            log.error("采集点【{}】脚本【{}】计算失败", point.getId(), script,e);
        }
        return 0D;
    }

    public static String computeMeterValue(Set<ExcelMeterInfo> meterInfos, String content) {
        if (!StringUtils.hasLength(content)) {
            return content;
        }
        boolean hasCompute = false;
        for (ExcelMeterInfo meterInfo : meterInfos) {
            if (meterInfo.getValue() == null) {
                continue;
            }
            if (!content.contains(meterInfo.getHolder())) {
                continue;
            }
            Map map = JSONUtil.parse(meterInfo).toBean(Map.class);
            if (map.get(meterInfo.getProperty()) != null) {
                Object v = map.get(meterInfo.getProperty());
                content = content.replace(meterInfo.getHolder(), EmsUtil.valueFormat(Double.parseDouble(v.toString())));
                hasCompute = true;
            }
        }
        if (!hasCompute) {
            return content;
        }
        try {
            if (content.contains("+") || content.contains("-") || content.contains("*") || content.contains("/")) {
                content = EmsUtil.valueFormat(Double.parseDouble(engine.eval(content).toString()));

            }
        }catch (Exception e) {
            log.error("脚本【{}】计算失败", content);
        }
        return content;
    }

    public static List<ExcelMeterInfo> matchMeters(String content) {
        List<ExcelMeterInfo> result = new ArrayList<>();
        if (content == null) {
            return result;
        }
        Pattern p = Pattern.compile(meterHolderPattern);
        Matcher m = p.matcher(content);
        Map<String, Double> map = new HashMap<>();
        while (m.find()) {
            String group = m.group(1);
            ExcelMeterInfo build = ExcelMeterInfo.build(group);
            if (build != null) {
                result.add(build);
            }
        }
        return result;
    }
}
